
import EventEmitter from "events";
class Modbus_RTU extends EventEmitter {
    constructor(addr,link) {
        super();
        EventEmitter.call(this);
        this.link = link;
        this.address = addr;
    }
    //CRC16校验
    crc16(buf) {
        let crc = 0xFFFF;
        for (let i = 0; i < buf.length; i++) {
            crc = crc ^ buf[i];
            for (let j = 0; j < 8; j++) {
                let f = crc & 1;
                crc = crc >> 1;
                if (f) {
                    crc = crc ^ 0xA001;
                }
            }
        }
        return crc;
    }

   setAddr(addr){
         this.address=addr;
    }

    makeDataPacket( code, dataAddress, length) {
        const codeLength = 6;
        const buf = Buffer.allocUnsafe(codeLength + 2); // add 2 crc bytes
        buf.writeUInt8(this.address, 0);
        buf.writeUInt8(code, 1);
        buf.writeUInt16BE(dataAddress, 2);
        buf.writeUInt16BE(length, 4);
        buf.writeUInt16LE(this.crc16(buf.slice(0, -2)), codeLength);
        return buf;
    }
    async FC03( dataAddress, length) {
        let buf = this.makeDataPacket(3,dataAddress, length);
        let rec= await this.link.SendData(buf);
        if(rec==null)
        { console.log("未接收到指令应答") ;return null;}

        //console.log('读寄存器应答',rec.toString('hex'));
        return rec 
    }

    /**
     * 根据modubs协议去掉粘包的数据
     * @param {*} dataAddress 
     * @param {*} length 
     * @returns 
     */
      subData(data) {
        if(data==null) return null;
        if(data.length<5) {
            console.log('数据长度小于5');
            return null;}
        else if(data[0]===0x63 && data[1]===0x6F && data[2]===0x6D && data[3]===0x33 && data.length>15) {
            //移除心跳部分的数据10字节
            data=data.subarray(10,data.length);
            console.log('移除心跳部分后的数据:',data.toString('hex'));
        }
        let len =data[2]+5;
        console.log(len,data.length)
        if(data.length<len) {
            console.log('数据长度小于应答长度',len,data.length);
            return null}
        else if(data.length==len) {
            return data}
        else{
            let buf = Buffer.allocUnsafe(len);
            data.copy(buf,0,0,len);
            return buf;
        }

     }






    async FC04( dataAddress, length) {
        let buf = this.makeDataPacket( 4,dataAddress, length);
        let rec= await this.link.SendData(buf);
        rec=this.subData(rec);
        if(rec==null)
        { console.log("未接收到指令应答") ;return null;}

        console.log('读寄存器应答',rec.toString('hex'));
        return rec 
    }
    async readFloat( dataAddress, length,code=4) {
        let rec =null;
        if(code==4){
            rec = await this.FC04( dataAddress, length*2);
        }
        else{
            rec = await this.FC03( dataAddress, length*2);
        }
        if(rec==null)
        { console.log( `链路${this.link.sn} 站号${this.address} ReadFloat 读取数据超时 `) ;return null;}
        console.log(`链路${this.link.sn} 站号${this.address} ReadFloat应答数据:,${rec.toString('hex')}`);
        let values = [];
        //console.log('读寄存器应答',rec.toString('hex'));
        if(rec.length>5){
            let data = rec.subarray(3, rec.length - 2);
            // console.log('解析的数据:',data.length, data.toString('hex'));
            let crc = rec.subarray(rec.length - 2, rec.length);    
            let crc_calc = this.crc16(rec.subarray(0, rec.length - 2));
            if (crc_calc == crc.readUInt16LE(0)) {
   
                for (let i = 0; i < data.length / 4; i++) {
                    let value = data.readFloatBE(i * 4);
                    values.push({ id: dataAddress + i, value: value });
                }
                return values;
            }
            else
            {
                console.log( `链路${this.link.sn} 站号${this.address} ReadFloat 返回数据CRC校验错误 `)  
            }
        }
        else
        {
            console.log( `链路${this.link.sn} 站号${this.address} ReadFloat 返回数据长度错误 返回数据长度${rec.length} `)
        }
       return values
    }

    async  FC06( dataAddress, value){
        const codeLength = 6;
        const buf = Buffer.allocUnsafe(codeLength + 2); // add 2 crc bytes
        buf.writeUInt8(this.address, 0);
        buf.writeUInt8(0x06, 1);
        buf.writeUInt16BE(dataAddress, 2);
        buf.writeUInt16BE(value, 4);
        buf.writeUInt16LE(this.crc16(buf.subarray(0, -2)), codeLength);
        let rec= await this.link.SendData(buf);
        if(rec==null)
        { console.log("未接收到指令应答") ;return null;}    
        //比较接收到的数据与发送的数据是否一致
        if(rec.equals(buf))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    async writeFloat( dataAddress, values) {
        console.log('写入数据:',JSON.stringify(values));
        let buf = Buffer.allocUnsafe(9+values.length*4);
        buf.writeUInt8(this.address, 0);
        buf.writeUInt8(0x10, 1);
        buf.writeUInt16BE(dataAddress, 2);
        buf.writeUInt16BE(values.length*2, 4);
        buf.writeUInt8(values.length*4, 6);
        let offset=7;
        for(let i=0;i<values.length;i++)
        {
            buf.writeFloatBE(values[i],offset);
            offset+=4;
        }
        buf.writeUInt16LE(this.crc16(buf.subarray(0, -2)), buf.length - 2);
        console.log(`链路${this.sn} 站号${this.address} writeFloat 发送数据:${buf.toString('hex')}`);
        let rec= await this.link.SendData(buf);
        if(rec==null)
        {console.log( `链路${this.link.sn} 站号${this.address} writeFloat 写入数据超时 写入数据:${buf.toString('hex')}`) ;return null;}   
        console.log(`链路${this.link.sn} 站号${this.address} writeFloat 应答数据:${rec.toString('hex')}`);
        //比较接收到的数据与发送的数据是否一致
        if(rec.length !=8)
        {
            return false;
        }
        if(rec[1]!=0x10) return false;
        //不校验寄存器地址
        // let addr = rec.readUInt16BE(2);
        // console.log('addr',addr,dataAddress);
        // if(addr != dataAddress) return false;
        return true;

    }

    async readUInit16( dataAddress, length) {
        let rec = await this.FC03( dataAddress, length);
        if(rec==null)
        { console.log("未接收到指令应答") ;return null;}
        //console.log('读寄存器应答',rec.toString('hex'));
        if(rec.length>5){
            let data = rec.subarray(3, rec.length - 2);
            console.log('解析的数据:',data.length, data.toString('hex'));
            let crc = rec.subarray(rec.length - 2, rec.length);    
            let crc_calc = this.crc16(rec.subarray(0, rec.length - 2));
            if (crc_calc == crc.readUInt16LE(0)) {
                let values = [];
                for (let i = 0; i < data.length / 2; i++) {
                    let value = data.readUInt16BE(i * 2);
                    values.push({ id: dataAddress + i, value: value });
                }
                console.log('values', values);
                return values;
            }
            else
            {
                console.log('crc error');
            }
        }
        else
        {
            console.log('数据长度小于5');
        }
        return null;
    }
    async writeUInt16( dataAddress, values) {
        let buf = Buffer.allocUnsafe(9+values.length*2);
        buf.writeUInt8(this.address, 0);
        buf.writeUInt8(0x10, 1);
        buf.writeUInt16BE(dataAddress, 2);
        buf.writeUInt16BE(values.length, 4);
        buf.writeUInt8(values.length*2, 6);
        let offset=7;
        for(let i=0;i<values.length;i++)
        {
            buf.writeUInt16BE(values[i],offset);
            offset+=2;
        }
        buf.writeUInt16LE(this.crc16(buf.subarray(0, -2)), buf.length - 2);
        let rec= await this.link.SendData(buf);
        if(rec==null)
        { console.log("未接收到指令应答") ;return null;}   
        console.log('写寄存器应答',rec.toString('hex')); 
        //比较接收到的数据与发送的数据是否一致
        if(rec.length !=8)
        {
            return false;
        }
        if(rec[1]!=0x10) return false;
        let addr = rec.readUInt16BE(2);
        if(addr != dataAddress) return false;
        return true;

    }

    async FC10( dataAddress, values) {
        let buf = Buffer.allocUnsafe(7 + values.length * 2);
        buf.writeUInt8(this.address, 0);
        buf.writeUInt8(16, 1);
        buf.writeUInt16BE(dataAddress, 2);
        buf.writeUInt16BE(values.length, 4);
        buf.writeUInt8(values.length * 2, 6);
        for (let i = 0; i < values.length; i++) {
            buf.writeUInt16BE(values[i], 7 + i * 2);
        }
        buf.writeUInt16LE(this.crc16(buf.subarray(0, -2)), buf.length - 2);
        let rec= await this.link.SendData(buf);
        if(rec==null)
        { console.log("未接收到指令应答") ;return null;}    
        //比较接收到的数据与发送的数据是否一致
        if(rec.length !=8)
        {
            return false;
        }
        if(rec[1]!=0x10) return false;
        let addr = rec.readUInt16BE(2);
        if(addr != dataAddress) return false;
        return true;


    }
}

export default Modbus_RTU;


// let  rtu = new Modbus_RTU(1,null);
// rtu.writeFloat(0x16,[2]);